package dicom.jpg.dto;

import com.rt.global.Assert;
import lombok.extern.slf4j.Slf4j;
import org.dcm4che3.data.Attributes;
import org.dcm4che3.image.ICCProfile;
import org.dcm4che3.imageio.plugins.dcm.DicomImageReadParam;
import org.dcm4che3.io.DicomInputStream;
import org.dcm4che3.util.SafeClose;

import javax.imageio.*;
import javax.imageio.stream.FileImageInputStream;
import javax.imageio.stream.FileImageOutputStream;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.Iterator;
import java.util.UUID;
import java.util.function.Predicate;
import java.util.stream.StreamSupport;

/**
 * <p>
 *
 * <p>版权所有:Copyright (c) 2021
 *
 * @author 周昭名
 * @version V1.0
 * @since 2022/8/11 17:13
 */
@Slf4j
public class Dcm2Jpg {

    private final ImageReader imageReader =
            ImageIO.getImageReadersByFormatName("DICOM").next();
    private ICCProfile.Option iccProfile = ICCProfile.Option.none;

    private Attributes attributes;
    private BufferedImage bufferedImage;

    private ImageWriter imageWriter;
    private ImageWriteParam imageWriteParam;

    /**
     * <p>将dcm文件转成jpg
     *
     * @param dcmFilePath  dcm文件路径
     * @param jpgFilePath  jpg文件路径
     * @param dcm2jpgParam 转换过程传参
     * @return 转换成功失败标识
     * @author 周昭名
     * @since 2022/8/9 18:43
     */
    public boolean dcm2JpgFile(String dcmFilePath, String jpgFilePath, Dcm2jpgParam dcm2jpgParam) {
        Assert.hasText(dcmFilePath, "源文件路径不能为空");
        Assert.hasText(jpgFilePath, "目标文件路径不能为空");
        Assert.notNull(dcm2jpgParam, "dcm2Jpg参数不能为空");


        File sourceFile = new File(dcmFilePath);
        File targetFile = new File(jpgFilePath);
        if (!sourceFile.exists()) {
            log.error("文件不存在，文件路径：{}", dcmFilePath);
            return false;
        }
        if (!sourceFile.isFile()) {
            log.error("文件是一个目录，不支持，请检查，路径：{}", dcmFilePath);
            return false;
        }

        targetFile.getParentFile().mkdirs();
        if (!readImageFromDicomInputStream(sourceFile, dcm2jpgParam)) {
            return false;
        }
        BufferedImage convertedBf = iccProfile.adjust(bufferedImage);
        initImageWriter(dcm2jpgParam.getQuality());
        return writeImage(targetFile, convertedBf);
    }

    /**
     * <p>输出jpg到response输出流中
     *
     * @author 周昭名
     * @since 2019/8/5 14:09
     */
    @Deprecated
    private void dcm2JpgResponse(HttpServletResponse res, BufferedImage bi, float quality) {
        res.setContentType("image/jpeg");
        res.setHeader("Etag", UUID.randomUUID().toString());
        res.setDateHeader("Last-Modified", System.currentTimeMillis());
        res.setHeader("Cache-Control", "max-age=7200");
        res.setDateHeader("expries",
                System.currentTimeMillis() + 1000 * 7200);
        OutputStream out = null;
        ImageWriter writer = null;
        try {
            out = res.getOutputStream();
            Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName("JPEG");
            if (iter.hasNext()) {
                writer = iter.next();
                try (ImageOutputStream os = ImageIO.createImageOutputStream(out)) {
                    writer.setOutput(os);
                    ImageWriteParam imageWriteParam = writer.getDefaultWriteParam();
                    imageWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
                    imageWriteParam.setCompressionQuality(quality);
                    BufferedImage convertedBf = iccProfile.adjust(bi);
                    writer.write(null, new IIOImage(bi, null, null), imageWriteParam);
                }
            }
        } catch (Throwable e) {
            log.error(e.getMessage());
        } finally {
            if (writer != null) {
                writer.dispose();
            }
            SafeClose.close(out);
        }
    }

    /**
     * <p>将dcm转成的jpg写入文件
     *
     * @param targetFile 目标文件
     * @return 写入成功标识
     * @author 周昭名
     * @since 2022/8/9 18:38
     */
    private boolean writeImage(File targetFile, BufferedImage bi) {
        try {
            RandomAccessFile raf = new RandomAccessFile(targetFile, "rw");
            raf.setLength(0);
            imageWriter.setOutput(new
                    FileImageOutputStream(raf));
            imageWriter.write(null, new
                    IIOImage(bi, null, null), imageWriteParam);
            return true;
        } catch (Exception e) {
            log.error("文件写入失败，目标文件：{},错误详情：{}", targetFile.getAbsolutePath(), e.getMessage());
        }
        return false;

    }

    /**
     * <p>
     *
     * @param dcm2jpgParam 外部传入的dcm转jpg的参数
     * @return 构造的ImageReadParam的参数
     * @author 周昭名
     * @since 2022/8/9 16:07
     */
    private ImageReadParam createImageReadParam(Dcm2jpgParam dcm2jpgParam) {
        DicomImageReadParam dicomImageReadParam =
                (DicomImageReadParam) imageReader.getDefaultReadParam();
        if (dcm2jpgParam.getWindowCenter() != null) {
            dicomImageReadParam.setWindowCenter(dcm2jpgParam.getWindowCenter());
        }
        if (dcm2jpgParam.getWindowWidth() != null) {
            dicomImageReadParam.setWindowWidth(dcm2jpgParam.getWindowWidth());
        }
        dicomImageReadParam.setAutoWindowing(dcm2jpgParam.getAutoWindowing());
        if (dcm2jpgParam.getIgnorePresentationLUTShape() != null) {
            dicomImageReadParam.setIgnorePresentationLUTShape(dcm2jpgParam.getIgnorePresentationLUTShape());
        }
        dicomImageReadParam.setWindowIndex(dcm2jpgParam.getWindowIndex() - 1);
        dicomImageReadParam.setVOILUTIndex(dcm2jpgParam.getVoiLUTIndex() - 1);
        dicomImageReadParam.setPreferWindow(dcm2jpgParam.getPreferWindow());
        dicomImageReadParam.setPresentationState(attributes);
        dicomImageReadParam.setOverlayActivationMask(dcm2jpgParam.getOverlayActivationMask());
        dicomImageReadParam.setOverlayGrayscaleValue(dcm2jpgParam.getOverlayGrayscaleValue());
        dicomImageReadParam.setOverlayRGBValue(dcm2jpgParam.getOverlayRGBValue());
        return dicomImageReadParam;
    }


    /**
     * <p>ImageInputStream读取文件
     *
     * @param file         dcm文件
     * @param dcm2jpgParam dcm转jpg参数
     * @return BufferedImage
     * @author 周昭名
     * @since 2022/8/9 15:11
     */
    public BufferedImage readImageFromImageInputStream(File file, Dcm2jpgParam dcm2jpgParam) throws IOException {
        ImageReadParam readParam = createImageReadParam(dcm2jpgParam);
        try (ImageInputStream iis = new FileImageInputStream(file)) {
            imageReader.setInput(iis);
            return imageReader.read(dcm2jpgParam.getFrame() - 1, readParam);
        }
    }

    /**
     * <p>DicomInputStream读取文件
     *
     * @param file         dcm文件
     * @param dcm2jpgParam dcm转jpg参数
     * @return 读取成功失败标识
     * @author 周昭名
     * @since 2022/8/9 15:11
     */
    public boolean readImageFromDicomInputStream(File file, Dcm2jpgParam dcm2jpgParam) {
        DicomInputStream dis = null;
        FileImageInputStream iis = null;
        try {
            //读取信息
            dis = new DicomInputStream(file);
            attributes = dis.readDatasetUntilPixelData();
            //读取dicom文件
            iis = new FileImageInputStream(file);
            ImageReadParam readParam = createImageReadParam(dcm2jpgParam);
            imageReader.setInput(iis);
            bufferedImage = imageReader.read(dcm2jpgParam.getFrame() - 1, readParam);
            return true;
        } catch (Exception e) {
            log.error("dcm文件读取失败");
        } finally {
            SafeClose.close(dis);
            SafeClose.close(iis);
        }
        return false;
    }


    /**
     * <p>ImageWriter初始化
     *
     * @param quality 图像质量取值范围0.0-1.0
     * @return 图像质量
     * @author 周昭名
     * @since 2022/8/9 15:31
     */
    public void initImageWriter(Float quality) {
        Iterator<ImageWriter> imageWriters =
                ImageIO.getImageWritersByFormatName("JPEG");
        if (quality == null) {
            quality = 0.9f;
        }
        if (!imageWriters.hasNext()) {
            throw new IllegalArgumentException("JPEG imageWriters 不存在!");
        }
        Iterable<ImageWriter> iterable = () -> imageWriters;
        imageWriter = StreamSupport.stream(iterable.spliterator(), false)
                .filter(matchClassName("com.sun.imageio.plugins.*"))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("JPEG imageWriters 不存在!"));
        imageWriteParam = imageWriter.getDefaultWriteParam();
        imageWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
        //imageWriteParam.setCompressionType("jpg");
        imageWriteParam.setCompressionQuality(quality);
    }

    private static Predicate<Object> matchClassName(String clazz) {
        Predicate<String> predicate = clazz.endsWith("*")
                ? startsWith(clazz.substring(0, clazz.length() - 1))
                : clazz::equals;
        return w -> predicate.test(w.getClass().getName());
    }

    private static Predicate<String> startsWith(String prefix) {
        return s -> s.startsWith(prefix);
    }
}

